<!DOCTYPE html>
<html>
	<head><meta charset="utf-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1">
	    <meta name="screen-orientation" content="landscape">
	    <style>
	    body{ text-align:center} 
	      #root {
	        width: 100%;
	        height: 600px;
	        background-color: #EEEEEE;
	      }
	    </style>
		<title>Points Transform</title>
	    <script type="text/javascript" src="js/three.js"></script>
	    <script type="text/javascript" src="js/dat.gui.min.js"></script>
	    <script type="text/javascript" src="js/Stats.js"></script>
	    <script type="text/javascript" src="js/tween.min.js"></script>
	    <script type="text/javascript">
	    	var root;
	    	var stage = {
	    		scene: null,
	    		renderer: null,
	    		camera: null,
	    		cameraHelper: null,
	    		light: null,
	    		plane: null,
	    		aixs: null,
	    		points: {run: false, mode: 0},
	    		sprites: {run: false, mode: 0},
	    		fps: 0,
	    		vertices: [],
	    		count: 0,
	    		init: function(){
					console.log('loading: stage - init - begin');
	    			var width = root.clientWidth;
	    			var height = root.clientHeight;

	    			this.scene = new THREE.Scene();

	    			this.renderer = new THREE.WebGLRenderer({alpha: true});
	    			this.renderer.setSize(width, height);
	    			this.renderer.setClearColor(0x000000, 1.0);
					this.renderer.shadowMap.enabled = true;
					this.renderer.shadowMap.soft = true;
					root.appendChild(this.renderer.domElement);
					root.appendChild(this.stats().domElement);

					this.camera = new THREE.PerspectiveCamera(75, width/height, 0.01, 10000);
					this.camera.position.set(10, 250, 350);
					this.camera.lookAt(this.scene.position);
					this.scene.add(this.camera);

					this.light = {
						ambientLight: new THREE.AmbientLight(0x1c1c1c),
						spotLight: new THREE.SpotLight(0x8c8c8c),
						pointLight: null
					}
					this.scene.add(this.light.ambientLight);
					this.light.spotLight.position.set(10, 300, 0);
					this.light.spotLight.castShadow = true;
					this.light.spotLight.shadow.camera.near = 0.2;
					this.light.spotLight.shadow.camera.far = 2000;
					this.light.spotLight.shadow.camera.fov = 30;
					this.light.spotLight.shadow.mapSize.width = 1024;
					this.light.spotLight.shadow.mapSize.height = 1024;
					this.scene.add(this.light.spotLight);

					this.cameraHelper = new THREE.CameraHelper(this.light.spotLight.shadow.camera);
					this.cameraHelper.name = 'cameraHelper';

					let cube = Geometry.cube;
	    			let sphere = Geometry.sphere;
	    			Geometry.text();
	    			// let text = Geometry.text_;
	    			this.count = 512;//Math.max(cube.vertices.length, sphere.vertices.length);
	    			for (var i = 0; i < this.count; i++) {
	    				if(cube.vertices[i]){
	    					this.vertices.push(new THREE.Vector3(cube.vertices[i].x, cube.vertices[i].y, cube.vertices[i].z));
	    				}else{
	    					this.vertices.push(new THREE.Vector3(cube.vertices[i % cube.vertices.length].x, cube.vertices[i % cube.vertices.length].y, cube.vertices[i % cube.vertices.length].z));
	    				}
	    				
	    			}

					this.setObj();
					this.scene.fog = new THREE.Fog(0xFFFFFF, 1.5);
					console.log('loading: stage - init - end');
	    		},
	    		setObj: function(){
	    			// this.plane = new THREE.Mesh(new THREE.PlaneGeometry(500, 500, 500, 500), new THREE.MeshLambertMaterial({color: 0xFFFFFF, side: THREE.DoubleSide}));

					// this.axis = new THREE.AxisHelper(20);
					// this.scene.add(this.axis);

					this.scene.add(Mesh.points());
	    		},
	    		pointCloud: function(e){
	    			let geo = null;
	    			switch(e){
	    				case 0:
	    					geo = Geometry.sphere;
	    					break;
	    				case 1:
	    					geo = Geometry.cube;
	    					break;
	    				case 2:
	    					geo = Geometry.torus;
	    					break;
	    				case 3:
	    					geo = Geometry.cylinder;
	    					break;
	    				case 4:
	    					geo = Geometry.text_;
	    					break;
	    			}
	    			for (var i = 0; i < this.count; i++) {
	    				let tween = new TWEEN.Tween(this.vertices[i]);
	    				tween.i = i;
	    				if(this.vertices[i] === undefined) continue;
	    				if(geo.vertices[i]){
	    					tween.to(geo.vertices[i], 3000).easing(TWEEN.Easing.Elastic.InOut);
	    					this.vertices[i] = new THREE.Vector3(geo.vertices[i].x, geo.vertices[i].y, geo.vertices[i].z);
	    				}else{
	    					tween.to(geo.vertices[i % geo.vertices.length], 3000).easing(TWEEN.Easing.Elastic.InOut);
	    					this.vertices[i] = new THREE.Vector3(geo.vertices[i % geo.vertices.length].x, geo.vertices[i % geo.vertices.length].y, geo.vertices[i % geo.vertices.length].z);
	    				}
	    				tween.start();
	    				tween.onUpdate(function(){
	    					stage.scene.getObjectByName('points').geometry.verticesNeedUpdate = true;
	    				});
	    				tween.onComplete(function(){
	    					stage.scene.getObjectByName('points').geometry.vertices[tween.i] = stage.vertices[tween.i];
	    				});
	    			}
	    			// this.scene.getObjectByName('points').geometry.vertices = stage.vertices;
	    			// this.scene.getObjectByName('points').geometry.verticesNeedUpdate = true;
	    		},
	    		stats_: null,
	    		stats: function(){
	    			if(this.stats_ === null){
	    				var stats = new Stats();
		    			stats.domElement.style.position = 'absolute';
		    			stats.domElement.style.left = '0px';
		    			stats.domElement.style.top = '0px';
		    			stats.domElement.style.width = '200px';
		    			this.stats_ = stats;
	    			}
	    			return this.stats_;
	    		},
	    		update: function(){
	    			this.stats().update();
	    			TWEEN.update();
	    		}
	    	};
	    	var Mesh = {
	    		points_: null,
	    		points: function(e){
	    			if(this.points_ === null){
	    				if(e === undefined) e = {};
	    				let name = e.name !== undefined ? e.name : 'points';
	    				let geometry = new THREE.Geometry();
	    				for (var i = stage.vertices.length ; i >= 0; i--) {
	    					if(!stage.vertices[i]) continue;
	    					geometry.vertices.push(stage.vertices[i]);
	    					geometry.colors.push(new THREE.Color(Math.random() * 0xffffff));
	    				}
	    				this.points_ = new THREE.Points(geometry, new THREE.PointsMaterial({vertexColors: true, color: 0xffffff, size:4}));
	    				this.points_.name = name;
	    			}
    				return this.points_;
	    		}
	    	}
	    	var Geometry = {
	    		cube: new THREE.CubeGeometry(200, 200, 200, 2, 2, 2),
	    		sphere: new THREE.SphereGeometry(100, 10, 20),
	    		torus: new THREE.TorusGeometry(100, 20, 10, 50),
	    		cylinder: new THREE.CylinderGeometry(100, 100, 200, 20, 10, false),
	    		text_: null,
	    		text: function(e){
	    			if(this.text_ === null){
	    				if(e === undefined) e = {};
						var loader = new THREE.FontLoader();
						loader.load('fonts/helvetiker_bold.typeface.json', 
							function(font){
								Geometry.text_ = new THREE.TextGeometry('Q', {
									font: font, 
									size:100, 
									height: 10,
									curveSegment: 90});
								}
						);
	    			}
	    		}
	    	}
	    	var Action = {
	    		camera: false,
	    		pointsMode: '方块',
	    	}
	    	var GUI = {
	    		gui: null,
	    		init: function(){
					console.log('loading: GUI - init - begin');
	    			var gui = new dat.GUI();
	    			gui.add(Action, 'camera').onChange(function(e){
	    				if(e){
	    					stage.scene.add(stage.cameraHelper);
	    				}else{
	    					stage.scene.remove(stage.scene.getObjectByName('cameraHelper'));
	    				}
	    			});
	    			let points = gui.addFolder('粒子系统');
	    			points.add(Action, 'pointsMode', ['球形', '方块', '管道', '圆柱', '文字']).onChange(function(e){
	    				switch(e){
	    					case '球形':
	    						stage.pointCloud(0);
	    					break;
	    					case '方块':
	    						stage.pointCloud(1);
	    					break;
	    					case '管道':
	    						stage.pointCloud(2);
	    					break;
	    					case '圆柱':
	    						stage.pointCloud(3)
	    					break;
	    					case '文字':
	    						stage.pointCloud(4);
	    					break;
	    				}
	    			});
	    			console.log('loading: GUI - init - end');
	    		}
	    	}
	    	function update(){
	    		stage.update();
	    	}
	    	function animation(){
	    		Mesh.points().position.x = Math.cos(stage.fps * Math.PI / 180) * 100;
	    		Mesh.points().rotation.y = stage.fps * Math.PI / 180;
	    		if(stage.fps++ >= 360) stage.fps = 0;
	    	}
	    	function render(){
	    		update();
	    		animation();
	    		stage.renderer.render(stage.scene, stage.camera);
				requestAnimationFrame(render);
	    	}

	    	function start(){
	    		console.log('loading: start begin');
	    		root = document.getElementById('root');
	    		stage.init();
	    		GUI.init();
	    		render();
	    		console.log('loading: start end');
	    	}

	    </script>
	</head>
	<body onload="start()">
		<div id='root'></div>
	</body>
</html>